<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WebGL2 Point 离屏渲染</title>
</head>

<body>
    <canvas id="c" width="1200" height="900"></canvas>
    <script>
        const canvas = document.getElementById("c");
        const gl = canvas.getContext("webgl2"); // 必须 webgl2
        if (!gl) alert("WebGL2 not supported");

        /* ---------- 着色器源码 ---------- */
        const vs = `#version 300 es
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_color;
layout(location = 2) in float a_size;
out vec3 v_color;
void main() {
    gl_Position = vec4(a_position, 1.0);
    gl_PointSize = a_size;
    v_color = a_color;
}`;

        const fs = `#version 300 es
precision mediump float;
in  vec3 v_color;
out vec4 fragColor;
void main() {
    fragColor = vec4(v_color, 1.0);
}`;

        // 用于屏幕绘制的着色器
        const screenVS = `#version 300 es
layout(location = 0) in vec2 a_position;
out vec2 v_texCoord;
void main() {
    gl_Position = vec4(a_position, 0.0, 1.0);
    v_texCoord = a_position * 0.5 + 0.5;
}`;

        const screenFS = `#version 300 es
precision mediump float;
uniform sampler2D u_texture;
in vec2 v_texCoord;
out vec4 fragColor;
void main() {
    fragColor = texture(u_texture, v_texCoord);
}`;

        /* ---------- 工具：编译 & 链接着色器 ---------- */
        function compile(type, src) {
            const s = gl.createShader(type);
            gl.shaderSource(s, src);
            gl.compileShader(s);
            if (!gl.getShaderParameter(s, gl.COMPILE_STATUS))
                throw gl.getShaderInfoLog(s);
            return s;
        }

        function createProgram(vertexShader, fragmentShader) {
            const prog = gl.createProgram();
            gl.attachShader(prog, vertexShader);
            gl.attachShader(prog, fragmentShader);
            gl.linkProgram(prog);
            if (!gl.getProgramParameter(prog, gl.LINK_STATUS))
                throw gl.getProgramInfoLog(prog);
            return prog;
        }

        function initWebGL() {
            // 创建主程序（用于点渲染）
            const mainProgram = createProgram(
                compile(gl.VERTEX_SHADER, vs),
                compile(gl.FRAGMENT_SHADER, fs)
            );

            // 创建屏幕绘制程序
            const screenProgram = createProgram(
                compile(gl.VERTEX_SHADER, screenVS),
                compile(gl.FRAGMENT_SHADER, screenFS)
            );

            // 获取屏幕程序中的纹理位置
            const textureLocation = gl.getUniformLocation(screenProgram, "u_texture");

            /* ---------- 随机生成点数据 ---------- */
            const N = 100; // 点数
            const data = new Float32Array(N * (3 + 3 + 1)); // xyz + rgb + size
            for (let i = 0, o = 0; i < N; ++i) {
                data[o++] = Math.random() * 2 - 1; // x
                data[o++] = Math.random() * 2 - 1; // y
                data[o++] = 0; // z
                data[o++] = Math.random(); // r
                data[o++] = Math.random(); // g
                data[o++] = Math.random(); // b
                data[o++] = Math.random() * 30 + 10; // size
            }

            /* ---------- 创建并填充 VBO ---------- */
            const vbo = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
            gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

            /* ---------- 设置顶点属性 ---------- */
            const stride = (3 + 3 + 1) * 4; // 7 floats

            // 创建全屏四边形的VBO
            const quadVertices = new Float32Array([
                -1.0, -1.0,
                 1.0, -1.0,
                -1.0,  1.0,
                 1.0,  1.0
            ]);

            const quadVBO = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, quadVBO);
            gl.bufferData(gl.ARRAY_BUFFER, quadVertices, gl.STATIC_DRAW);

            // 创建VAO用于点渲染
            const pointVAO = gl.createVertexArray();
            gl.bindVertexArray(pointVAO);
            gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 绑定点VBO
            gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, 0); // a_position
            gl.vertexAttribPointer(1, 3, gl.FLOAT, false, stride, 3 * 4); // a_color
            gl.vertexAttribPointer(2, 1, gl.FLOAT, false, stride, 6 * 4); // a_size
            gl.enableVertexAttribArray(0);
            gl.enableVertexAttribArray(1);
            gl.enableVertexAttribArray(2);

            // 创建VAO用于屏幕渲染
            const quadVAO = gl.createVertexArray();
            gl.bindVertexArray(quadVAO);
            gl.bindBuffer(gl.ARRAY_BUFFER, quadVBO); // 绑定四边形VBO
            gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(0);
            gl.disableVertexAttribArray(1);
            gl.disableVertexAttribArray(2);

            /* ---------- 创建帧缓冲对象 ---------- */
            const framebuffer = gl.createFramebuffer();
            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

            // 创建纹理附件
            const texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

            // 将纹理附加到帧缓冲区
            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

            // 检查帧缓冲区是否完整
            if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
                throw "Framebuffer is not complete";
            }

            /* ---------- 离屏渲染 ---------- */
            // 绑定帧缓冲区进行离屏渲染
            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
            gl.viewport(0, 0, canvas.width, canvas.height);
            gl.clearColor(0, 0, 0, 1);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.useProgram(mainProgram);
            gl.bindVertexArray(pointVAO); // 使用点VAO
            gl.drawArrays(gl.POINTS, 0, N);

            /* ---------- 屏幕渲染 ---------- */
            // 绑定默认帧缓冲区（屏幕）进行最终渲染
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
            gl.viewport(0, 0, canvas.width, canvas.height);
            gl.clearColor(0.1, 0.1, 0.1, 1); // 使用不同背景色以区分
            gl.clear(gl.COLOR_BUFFER_BIT);
            
            // 使用屏幕程序绘制全屏四边形，并将离屏渲染的纹理作为输入
            gl.useProgram(screenProgram);
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.uniform1i(textureLocation, 0);
            gl.bindVertexArray(quadVAO); // 使用四边形VAO
            gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
        }

        initWebGL();
    </script>
</body>

</html>